package nacos

import (
	"context"
	"fmt"
	"sync"
	"time"

	"go-main/library/log"
	"google.golang.org/grpc/resolver"

	"github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client"
	"github.com/nacos-group/nacos-sdk-go/v2/model"
	"github.com/nacos-group/nacos-sdk-go/v2/vo"
)

const (
	Name = "nacos"
)

type ResolverBuilder struct {
	Client naming_client.INamingClient
}

// Scheme returns the scheme supported by this resolver.
// Scheme is defined at https://github.com/grpc/grpc/blob/master/doc/naming.md.
func (r *ResolverBuilder) Scheme() string {
	return Name
}

// Build gRPC dial calls Build synchronously, and fails if the returned error is
// not nil.
func (r *ResolverBuilder) Build(target resolver.Target, cc resolver.ClientConn,
	opts resolver.BuildOptions) (resolver.Resolver, error) {
	ctx, cancel := context.WithCancel(context.Background())
	nr := nacosResolver{
		client: r.Client,
		cc:     cc,
		ch:     make(chan []model.Instance, 1),
		rn:     make(chan struct{}, 1),
		target: target,
		ctx:    ctx,
		cancel: cancel,
	}
	nr.wg.Add(1)
	//go r.flush()
	go nr.watcher()
	nr.ResolveNow(resolver.ResolveNowOptions{})
	return &nr, nil
}

type nacosResolver struct {
	//resolver.Resolver
	client      naming_client.INamingClient
	cc          resolver.ClientConn
	ch          chan []model.Instance
	rn          chan struct{}
	target      resolver.Target
	wg          sync.WaitGroup
	balanceOnce sync.Once
	ctx         context.Context
	cancel      context.CancelFunc
}

func (nr *nacosResolver) watcher() {
	defer nr.wg.Done()
	var instances []model.Instance
	var err error
	_ = nr.client.Subscribe(&vo.SubscribeParam{
		ServiceName: nr.target.Endpoint,
		GroupName:   nr.target.Authority,
		SubscribeCallback: func(services []model.Instance, err error) {
			//fmt.Printf("callback return services:%s \n", util.ToJsonString(services))
			nr.ch <- services
		},
	})

	ticker := time.NewTicker(time.Second * 5)
	for {
		select {
		case instances = <-nr.ch:
		case <-nr.rn:
		case <-ticker.C:
			instances, err = nr.client.SelectAllInstances(vo.SelectAllInstancesParam{
				ServiceName: nr.target.Endpoint,
				GroupName:   nr.target.Authority,
				Clusters:    []string{"cluster-a"},
			})
			if err != nil {
				log.Infof("resolver SelectAllInstances err (%+v)", err)
				nr.cc.ReportError(err)
			}
		}
		if len(instances) > 0 {
			nr.flush(instances)
		}
	}
}

func (nr *nacosResolver) flush(instances []model.Instance) {
	var addrs []resolver.Address
	for _, v := range instances {
		addrs = append(addrs, resolver.Address{
			Addr:               fmt.Sprintf("%s:%d", v.Ip, v.Port),
			ServerName:         v.ServiceName,
			Attributes:         nil,
			BalancerAttributes: nil,
		})
	}

	state := resolver.State{
		Addresses:     addrs,
		ServiceConfig: nil,
		Attributes:    nil,
	}

	/*nr.balanceOnce.Do(func() {
		state.ServiceConfig = &serviceconfig.ParseResult{

			//lint:ignore SA1019 we want to keep the original config here
			Config: &grpc.ServiceConfig{
				LB: proto.String("p2c"),
			},
		}
	})*/
	//log.Info("address is (%+v)", addrs)
	if err := nr.cc.UpdateState(state); err != nil {
		log.Errorf("resolver update state err(%+v),instances (%+v)", err, instances)
	}
}

// ResolveNow The method is called by the gRPC framework to resolve the target name
func (nr *nacosResolver) ResolveNow(opt resolver.ResolveNowOptions) { // 立即resolve，重新查询服务信息
	select {
	case nr.rn <- struct{}{}:
	default:
	}
}

// Close resolver closed
func (nr *nacosResolver) Close() {
	nr.cancel()
}
